﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Entities;
using Unity.Transforms;
using Unity.Mathematics;
using Ecset = Unity.Entities.Entity;
using Ecsmgr = Unity.Entities.EntityManager;

public struct QData
{
    public Ecset entityData;
    public Vector2 coord;
    public Vector2Int coordInt;
    public Chunk belongToChunk;
    public uint id;
    //private VoxelComponentData restore;
    public void Dispose()
    {
        //restore = TerrianManager.defaultManager.GetComponentData<VoxelComponentData>(entityData);
        //TerrianManager.defaultManager.DestroyEntity(entityData);
        TerrianManager.defaultManager.SetEnabled(entityData,false);
    }
    public void Revive()
    {
        TerrianManager.defaultManager.SetEnabled(entityData, true);
        //entityData = VoxelStatic.Spawn(TerrianManager.defaultManager, coord.x, coord.y, VoxelStatic.voxelValues[restore.elem]);
        //TerrianManager.defaultManager.SetComponentData<VoxelComponentData>(entityData, restore);
    }

}

/*
-----------------------------
|     1       |      3      |
|             |             |
|---------------------------|
|     0       |      2      |
|             |             |
-----------------------------
*/


public class TerrianQuadTree
{
    public const bool QTREEDEBUG = false;
    public const bool QTREEDEBUGDRAW = false;
    private const int MAXCHILD = 4;
    private const int MAXDEEP = 6;   //当前渲染区块拆分 4^4
    public static Ecsmgr entityManager = TerrianManager.defaultManager;
    public QuadNode rootNode;
    public enum Sub
    {
        LeftDown = 0,
        LeftUp,
        RightDown,
        RightUp
    }

    public TerrianQuadTree(Bounds bbound)
    {
        if(QTREEDEBUG)
        {
            //Debug.Log("START BUILDING QUADTREE....");
        }
        rootNode = new QuadNode(bbound, 0, null);         //chunk根结点算0层深
    }

    public class QuadNode
    {
        private int depth;

        public Bounds bound;    

        public QuadNode[] subNode = new QuadNode[4];       //new集合和new集合新元素 不一样 会导致index++报错
        public QuadNode parentNode = null;  //不知道有没有用
        public int childNum;

        private QData data;
        public List<QData> EntityStore;
        private int subTreeDataCnt;     //子树中保存了多少实体

        /// <summary>
        /// 结点构造函数，生成包络块
        /// </summary>
        /// <param name="bbound"></param>
        /// <param name="dep"></param>
        /// <param name="parent"></param>
        public QuadNode(Bounds bbound, int dep, QuadNode parent)
        {
            this.bound = bbound;
            this.depth = dep;
            this.childNum = 0;
            this.EntityStore = new List<QData>();
            this.parentNode = parent;
            this.subTreeDataCnt = 0;
            
            /*if(QTREEDEBUGDRAW)
            {
                float tmpXL = (bound.center - bound.size / 2).x;
                float tmpYD = (bound.center - bound.size / 2).y;
                float tmpXR = (bound.center + bound.size / 2).x;
                float tmpYU = (bound.center + bound.size / 2).y;
                Debug.DrawLine(new Vector3(tmpXL, tmpYU, 0), new Vector3(tmpXL, tmpYD), Color.yellow, int.MaxValue);
                Debug.DrawLine(new Vector3(tmpXL, tmpYD, 0), new Vector3(tmpXR, tmpYD), Color.yellow, int.MaxValue);
                Debug.DrawLine(new Vector3(tmpXR, tmpYD, 0), new Vector3(tmpXR, tmpYU), Color.yellow, int.MaxValue);
                Debug.DrawLine(new Vector3(tmpXR, tmpYU, 0), new Vector3(tmpXL, tmpYU), Color.yellow, int.MaxValue);
                Debug.Log("NODE CREATE SUCCESS, WITH DEPTH " + dep);
                Debug.Log("BOUNDSIZE " + this.bound.size + " BOUNDCENTER " + this.bound.center);
            }*/
        }

        /// <summary>
        /// 插入新结点
        /// </summary>
        /// <param name="data"></param>
        public void Insert(QData data)
        {
            if (QTREEDEBUG)
            {
                //Debug.Log("TRY INSERT NODE WITH COORD " + data.coord + ".....");
            }
            
            bool bChild = false;
            QuadNode tmpNode = null;
            if (this.depth < MAXDEEP && this.childNum == 0)
            {
                this.CreateNewSubNodes();
            }
            else
            {
                if (QTREEDEBUG)
                {
                    //Debug.Log("REACH MAX DEPTH...STORE IN CURRENT LEAF");
                }
            }
            if (childNum == 4)
            {
                if (QTREEDEBUG)
                {
                    //Debug.Log("START SEARCHING SUBNODES");
                }
                
                for(int i=0;i<subNode.Length;i++)
                {
                    if (QTREEDEBUG)
                    {
                        //Debug.Log("CHECKING SUBNODE " + (Sub)i);
                    }
                    
                    QuadNode qNode = subNode[i];
                    if (qNode == null)
                    {
                        if (QTREEDEBUG)
                        {
                           //Debug.LogWarning("SUBNODE NULL");
                        }
                        continue;
                    }
                    else if(isContain(qNode, entityManager, data))
                    {
                        //Debug.Log("SUBNODE MATCH");
                        if(tmpNode != null)
                        {
                            bChild = false;
                            break;
                        }
                        tmpNode = qNode;
                        bChild = true;
                        //Debug.Log("插入至" + (Sub)i);
                        break;
                    }
                }
            }
            if(bChild)
            {
                tmpNode.Insert(data);
            }
            else                        
            {
                if(QTREEDEBUG)
                {
                    //Debug.Log("LIST ADD");
                }
                if(EntityStore.Contains(data))
                {
                    //Debug.LogError("data已经存在");
                }
                EntityStore.Add(data);     //四个区块都不包含则保存在父节点上
                QuadNode p = this;
                while(p.depth >= 0 && p!=null)
                {
                    p.subTreeDataCnt++;
                    if (p.depth == 0)
                    {
                        break;
                    }
                    else
                    { 
                        p = p.parentNode;
                    }
                }
            }
        }


        /// <summary>
        /// 删除结点
        /// </summary>
        /// <param name="data"></param>
        /// 
        private void Delete(QData data, QuadNode nodeToDelete, bool async = false)
        {
            if (nodeToDelete == null)
            {
                Debug.LogWarning("TRYING TO DELETE A NODE NOT EXISTS");
            }
            nodeToDelete.EntityStore.Remove(data);

            QuadNode p = nodeToDelete;

            if(async == false)
            {
                if(!entityManager.HasComponent<FadeTagComponentData>(data.entityData))
                {
                    entityManager.AddComponent<FadeTagComponentData>(data.entityData);
                    //Debug.Break();
                    TerrianManager.waitForDelete = true;
                }
            }
            else
            {
                entityManager.DestroyEntity(data.entityData);
            }

            QuadNode pp = nodeToDelete;
            while (pp != null && pp.depth >= 0)
            {
                pp.subTreeDataCnt--;
                pp = pp.parentNode;
            }

            while(p.parentNode != null && p.parentNode.subTreeDataCnt == 0)
            {
                for(int i=0; i<4; i++)
                {
                    p.parentNode.subNode[i] = null;
                }
                p.parentNode.childNum = 0;
                p = p.parentNode;
            }
        }

        public void Delete(QData data)
        {
            QuadNode nodeToDelete = Search(data);
            if(nodeToDelete == null)
            {
                Debug.LogError("Try to delete an entity that does not exist");
                return;
            }
            Delete(data, nodeToDelete);
        }
        
        public void Delete(Vector2 coordToDelete, bool async = false)
        {
            QuadNode nodeToDelete = Search(coordToDelete);
            if (nodeToDelete == null)
            {
                Debug.LogError("Try to delete an entity that does not exist");
                return;
            }
            QData qd = nodeToDelete.EntityStore.Find(x => x.coord == coordToDelete);
            Delete(qd, nodeToDelete, async);
        }

        public void DisposeEntity(QuadNode node)
        {
            Queue<QuadNode> toSearch = new Queue<QuadNode>();
            toSearch.Enqueue(this);
            QuadNode p = this;
            while (!(toSearch.Count == 0 && p == null))
            {
                if (toSearch.Count != 0)
                {
                    p = toSearch.Dequeue();
                }
                else
                {
                    p = null;
                    continue;
                }
                for (int i = 0; i < MAXCHILD; i++)
                {
                    if (p.subNode[i] != null && p.subNode[i].subTreeDataCnt != 0)
                    {
                        toSearch.Enqueue(p.subNode[i]);
                    }
                }
                foreach (var e in p.EntityStore)
                {
                    e.Dispose();
                }
            }
        }
        public void GenerateEntity(QuadNode node)
        {
            Queue<QuadNode> toSearch = new Queue<QuadNode>();
            toSearch.Enqueue(this);
            QuadNode p = this;
            while (!(toSearch.Count == 0 && p == null))
            {
                if (toSearch.Count != 0)
                {
                    p = toSearch.Dequeue();
                }
                else
                {
                    p = null;
                    continue;
                }
                for (int i = 0; i < MAXCHILD; i++)
                {
                    if (p.subNode[i] != null && p.subNode[i].subTreeDataCnt != 0)
                    {
                        toSearch.Enqueue(p.subNode[i]);
                    }
                }
                foreach (var e in p.EntityStore)
                {
                    e.Revive();
                }
                if (toSearch.Count != 0)
                {
                    p = toSearch.Dequeue();
                }
                else
                {
                    p = null;
                }
            }
        }

        /// <summary>
        /// 按数据或坐标搜索结点，返回QuadNode
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public QuadNode Search(QData data)
        {
            if (!this.bound.Contains(data.coord))
            {
                return null;
            }

            if (this.EntityStore.Contains(data))
            {
                return this;
            }
            else
            {
                int index = 0;
                while (this.subNode[index] != null && !this.subNode[index].bound.Contains(data.coord) && index < 4)
                    index++;
                return subNode[index].Search(data);
            }
        }
        public QuadNode Search(Vector2 coordToSearch)
        {
            //Debug.Log("SEARCH " + coordToSearch.ToString());
            if (!this.bound.Contains(coordToSearch))
            {
                return null;
            }

            foreach(var e in this.EntityStore)
            {
                if (math.abs(e.coord.x - coordToSearch.x ) <= TerrianManager.HALFVOXELSIZE && math.abs(e.coord.y - coordToSearch.y) <= TerrianManager.HALFVOXELSIZE)
                    return this;
            }

            int index = 0;
            for(index = 0;index<3;index++)
            {
                if(subNode[index] == null)
                {
                    continue;
                }
                else if (this.subNode[index].bound.Contains(coordToSearch))
                {
                    break;
                }
            }
            if(subNode[index] == null)
            {
                return null;
            }
            return subNode[index].Search(coordToSearch);
        }

        /// <summary>
        /// 给定一个满的结点和一个附近坐标，寻找该区域中所有的临近结点，tolerant>=1.0f
        /// </summary>
        /// <param name="coordToSearch"></param>
        /// <param name=""></param>
        /// <returns></returns>
        public List<QData> GetSubNearNodes(Vector2 coordToSearch, float tolerantRate)
        {
            List<QData> ans = new List<QData>();
            foreach(var e in this.EntityStore)
            { 
                if(Vector2.Distance(coordToSearch,e.coord) <= TerrianManager.HALFVOXELSIZE * tolerantRate)  //可删
                {
                    ans.Add(e);
                }
            }
            int index = 0;
            for (index = 0; index < 3; index++)
            {
                if (subNode[index] == null)
                {
                    continue;
                }
                else if (Vector2.Distance(subNode[index].bound.center,coordToSearch) <= TerrianManager.HALFVOXELSIZE * tolerantRate)
                {
                    ans.AddRange(subNode[index].GetSubNearNodes(coordToSearch, tolerantRate));
                }
            }
            return ans;
        }

        /*public QuadNode FuzzySearch(Vector2 coordToSearch, float TolerantRange)
        {
            if (!this.bound.Contains(coordToSearch))
            {
                return null;
            }

            foreach (var e in this.EntityStore)
            {
                if (math.abs(e.coord.x - coordToSearch.x) <= TerrianManager.HALFVOXELSIZE * TolerantRange && math.abs(e.coord.y - coordToSearch.y) <= TerrianManager.HALFVOXELSIZE * TolerantRange)
                    return this;
            }

            int index = 0;
            for (index = 0; index < 3; index++)
            {
                if (subNode[index] == null)
                {
                    continue;
                }
                else if (math.abs(coordToSearch.x - subNode[index].bound.center.x) <= subNode[index].bound.extents.x && math.abs(coordToSearch.y - subNode[index].bound.center.y) <= subNode[index].bound.extents.y)
                //this.subNode[index].bound.Contains(coordToSearch)
                //math.abs(coordToSearch.x - subNode[index].bound.center.x) <= subNode[index].bound.extents.x * TolerantRange && math.abs(coordToSearch.y - subNode[index].bound.center.y) <= subNode[index].bound.extents.y * TolerantRange
                {
                    break;
                }
            }
            return subNode[index].FuzzySearch(coordToSearch, TolerantRange);
        }*/

        [System.Obsolete]
        public QuadNode Search(Vector3Int coordToSearch)
        {
            return Search(Geo.ChunkCoordIntToWorld(coordToSearch));
        }
        
        
        public bool isContain(QuadNode qNode, Ecsmgr mgr, QData ddata)   //没存在Node的List里的时候用这个方法
        {
            Translation trans = mgr.GetComponentData<Translation>(ddata.entityData);
            if (qNode.bound.Contains(trans.Value))  //可以绕开manager了
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        private void CreateNewSubNodes()
        {
            int index = 0;
            for(int i=-1;i<2;i+=2)
            {
                for(int j=-1;j<2;j+=2)
                {
                    Vector3 centerOffset = new Vector3(bound.size.x/4.0f * i, bound.size.y/4.0f*j, 0);
                    Vector3 cSize = new Vector3(bound.size.x / 2.0f, bound.size.y / 2.0f, bound.size.z);
                    Bounds cBound = new Bounds(bound.center + centerOffset, cSize);
                    subNode[index++] = new QuadNode(cBound, depth + 1, this);
                }
            }
            this.childNum += 4;
        }

        /// <summary>
        /// 获得Boxs，用于给EnginePhysic生成碰撞体
        /// </summary>
        /// <returns></returns>
        public List<QuadNode> GetFullNodes()
        {
            var ans = new List<QuadNode>();
            //pow性能
            if (this.subTreeDataCnt == math.pow(4,MAXDEEP - this.depth))
            {
                ans.Add(this);
            }
            else
            {
                for(int i=0;i<MAXCHILD;i++)
                {
                    if (this.subNode[i] != null)
                        ans.AddRange(this.subNode[i].GetFullNodes());
                }
            }
            return ans;
        }

        public void NodeLog()
        {
            Debug.Log("Total Data: " + this.subTreeDataCnt);
            for (int i = 0; i < 4; i++)
            {
                if(subNode[i]!=null)
                {
                    Debug.Log(this.subNode[i].subTreeDataCnt);
                }
            }
        }
    }

    public List<QData> GetAllSubData(QuadNode quadNode)
    {
        List<QData> ans = new List<QData>();
        ans.AddRange(quadNode.EntityStore);
        if(quadNode.childNum == 4)
        {
            foreach (var sub in quadNode.subNode)
            {
                ans.AddRange(GetAllSubData(sub));
            }
        }
        return ans;
    }
    /*public List<QData> GetDataInArea<T>(T area) where T : Geo.GeoArea
    {
        QuadNode p = this.rootNode;
        bool bfsFlag = true;
        while(bfsFlag)
        {
            bool ctn = false;
            int i = 0;
            for (i = 0; i < 4; i++)
            {
                if (isPointSetInBound(p.subNode[i].bound, area.pointSet))
                {
                    ctn = true;
                    break;
                }
            }
            if(ctn == false)
            {
                bfsFlag = false;
            }
            else
            {
                p = p.subNode[i];
            }
        }
        return GetAllSubData(p);
    }*/

    public static bool isPointSetInBound(Bounds bounds, List<Vector2> set)
    {
        foreach (var p in set)
        {
            if (!bounds.Contains(p))
                return false;
        }
        return true;
    }

}

